Skip to content

fix(sync): adopt valid replacements during save readback - #487

Merged
chhoumann merged 2 commits into
masterfrom
fix/false-save-notice
Oct 15, 2026
Merged

chhoumann merged 2 commits into
masterfrom
fix/false-save-notice

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

A valid sync replacement between a save and its readback now merges pending edits without the false "could not save" Notice. Real failures still notify and retry. Private feed URLs are protected before a strict save completes.

Verification

  • Passed npm run lint, npm run format:check, npm run typecheck, and npm run build.
  • Passed npm run test with 2,745 tests, npm run test:tooling with 35 tests, and npm run docs:build with all links and 112 legacy fragments valid.
  • Linux isolated Obsidian 1.14.4 passed all 221 e2e tests on a fresh vault. The full 1.13.7 run passed 220 of 221 and exposed a settings-test setup race. Adding a completed-save wait made all 9 settings tests pass on both versions. Both new readback journeys passed in both full runs. Commands were npm run test:e2e and npm run test:e2e -- settings-tab.test.ts, with PODNOTES_OBSIDIAN_APP=/opt/Obsidian/obsidian for 1.13.7. The sync fixture is https://example.com/synced.xml; private tests use synthetic URLs under private.example.
  • Under 16 CPU workers, captured readback bytes show 6 false Notices in 100 master runs and 0 in 100 final runs. All 100 final iterations passed, with one expected empty-readback Notice. The evidence report also preserves earlier unclassified Notices and a loop assertion independently shown to follow the existing same-library merge policy.
  • The generated 60-second Fixtures/Local Fixture.wav start, pause, seek, resume, pause session kept three completed writes on both builds, with zero failures. npm run size passed at 102,441 gzip bytes, +226 bytes, with 559 bytes below the limit.
  • Before screenshot and after screenshot. Evidence archive includes the original failures, rerun logs, mutation scripts, and independent verdicts.

All 14 wrong alternatives failed tests. Full test names and logs are in the evidence archive.

Mutation Failing assertion or test
Accept every mismatch Replacement merge and real save-failure tests
Drop the external change Merged disk contains the Synced feed
Compare with old base Unchanged pre-write disk fails once and retries
Drop in-flight overlay Undoing a local edit during save stays undone
Accept migration replacement Startup migration fails closed
Repair an invalid readback Repaired readback remains a failed save
Omit raw identity Unchanged private file makes one failed attempt
Compare normalized identity Unchanged private file makes one failed attempt
Never rewrite normalized data Raw private address is absent after strict save
Always rewrite replacements Already-saved public replacement adds no write
Ignore the rewrite outcome Raw private address is absent after strict save
Classify a later reread Both captured library A and later library B survive
Reject normalized equality even when rewrite is required A copied private URL is protected again without a Notice
Delete the generic unchanged-file guard Unchanged pre-write disk fails once and retries

Release impact

No schema, settings format, public API, or migration change.

Docs at release

In docs/src/content/docs/sync.md, under "Changes on two devices", describe merging a valid replacement observed during save readback without a failure Notice, with a follow-up write when local edits remain. Retain the warning for failed or unreadable writes. Published docs stay unchanged in this PR.

Fixes #481

Note

Adopt valid data.json replacements during save readback in DataFileWriter

  • Reworks the save path in writer.ts into a merge loop: after each write, the file is read back, and a valid document written by another writer (e.g. sync) is merged with local edits and written again until the write is verified
  • Adds a WriteOutcome type (verified or different) and updates DataFileAccess to return it from writes; an unchanged readback is treated as a failed save and retried
  • Extracts decoding/repair/refusal logic from the access adapter into PodNotes.decodeDataFile in main.ts; startup migration now rejects when its write is not verified
  • Behavioral Change: unreadable, newer-schema, or unsafe-to-repair post-write readbacks now fail the save instead of silently succeeding; external settings changes route through the same adoption path. Check PodNotes.dataFileAccess, PodNotes.loadSettings, and DataFileWriter.writeMerged

Macroscope summarized 3acdd4f.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying podnotes with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3acdd4f
Status: ✅  Deploy successful!
Preview URL: https://208e20b4.podnotes.pages.dev
Branch Preview URL: https://fix-false-save-notice.podnotes.pages.dev

View logs

@chhoumann chhoumann linked an issue Oct 15, 2026 that may be closed by this pull request
@chhoumann
chhoumann merged commit 8bc048a into master Oct 15, 2026
18 checks passed
@chhoumann
chhoumann deleted the fix/false-save-notice branch October 15, 2026 08:17
chhoumann added a commit that referenced this pull request Oct 15, 2026
Ports the Docs at release sections of #486 and #487. Notes that the
player, the Transcript panel, and the podcast menu open go beside the
PodNotes view in the main area. A file that sync replaces during a save
merges without the save notice.
chhoumann added a commit that referenced this pull request Oct 15, 2026
* docs(transcripts): describe published transcripts and the from-audio command

* docs: port sync, private feed, capture, note, and local file changes

* docs: port player, API, template, settings, and import changes

* docs(readme): mention free transcripts from the feed

* docs: apply round 1 review fixes and move save problems to their own page

* docs: apply round 2 review fixes

* docs: apply round 3 review fixes

* docs(settings): keep the Search Podcasts section until the UI docs pass

* docs: apply round 4 review fixes

* docs: apply round 5 review fixes

* docs: apply round 6 review fixes

* docs: apply round 7 review fixes

* docs: apply round 8 review fixes

* docs: apply round 9 review fixes

* docs: apply round 10 review fixes

* docs: apply round 11 review fixes

* docs: apply round 12 review fixes

* docs: apply round 13 review fixes

* docs: apply round 14 review fixes

* docs: apply round 15 review fixes

* docs: describe the v3 view, player, and settings for 2.22.0

Rewrites the pages that described the 2.21.1 view and settings, adds a page for the player, and rewrites docs/scripts/screenshots.mjs on the verify skill's screens tooling with the fixture library. The screenshots are provisional until the final visual pass merges.

* docs: apply round 1 review fixes

Adds an Update to 2.22.0 page, fixes the loaded-episode and offline wording, makes in-page links site-absolute, and adds a phone twin for the transcript shot.

* docs: apply round 2 review fixes

Corrects what happens after importing a 2.21.1 settings file, fits alt texts to their phone crops, and crops the first-run shot.

* docs: apply round 3 review fixes

* docs: apply round 4 review fixes

* docs: describe the headset buttons as new for 2.21.1 users

* docs: describe the final v3 UI and capture the final screenshots

Applies #485's Docs at release items: Remove from Local files, Exit fullscreen, Open note in the Transcript panel, Set feed address off while offline, and the Download path warning.

* docs: apply round 5 review fixes

Name icon buttons by what a reader sees now that #485 gave them per-episode
names, split the no-transcript paragraph into a list, scope the iPhone
fullscreen sentence, and end phone note crops below the last whole block.

* docs: share the screenshot script's scene helpers

Move the seed plan, the docs' fixture library, episode notes made with
PodNotes' commands, and the instance's CDP channel out of screenshots.mjs
into scene.mjs, so the clip recorder can use them too.

* docs: record the guide clips with videos.mjs

node docs/scripts/videos.mjs records five clips in the isolated Obsidian
with real mouse and touch input and a drawn pointer, at 2x through
Electron's zoom, and writes H.264 MP4s with WebP posters and dark twins
to docs/public/videos.

* docs: show the guide clips on their pages

Getting started, Capture timestamps, Use the player, Transcribe episodes,
Use the queue and playlists, and Use private feeds each get a clip after
a sentence that says what it shows. A clip plays muted on a loop, and
readers who prefer reduced motion get its poster and controls instead.
The docs README now describes the current screenshot script.

* docs: apply the clip review fixes

Pause clips when the reader turns on reduced motion, scroll Up next in
the phone clip so the whole new queue shows, hide the drawn mouse arrow
in touch clips, keep times out of clip alt text, where the site's
directive syntax drops them, and take posters after the click ring fades.

* docs: end the queue clip on the new order and clear the feed poster

The phone clip now holds on Up next with the moved episode first, scrolls
to the last episode, and scrolls back, and the private feed's poster has
the pointer off the pasted address.

* docs: open notes beside the player and merge synced files during a save

Ports the Docs at release sections of #486 and #487. Notes that the
player, the Transcript panel, and the podcast menu open go beside the
PodNotes view in the main area. A file that sync replaces during a save
merges without the save notice.

* docs: record the queue clip without the grip's focus ring

Since #486 a touch drag leaves no focus ring on the queue grip.

* docs: say where a note opens in each layout

Round 1 review: readers could not tell where a note opens with the view
in a sidebar. One section on the notes page now covers the sidebar, the
main area or a popout, phones, and a note that opens while another tab
is active. Fix save problems and Sync now name both causes of the save
notice.

* docs: name each case of where a note opens

Round 2 review: Where a note opens now says which tab a note takes in
each layout, includes commands run from a note, and does not imply that
a capture opens the note. Getting started and the update page note the
phone exception. Sync says that a change written over between the read
and the write shows no notice.

* docs: note that 2.21.1 and 2.22.0 don't share an episode's later progress
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A sync write during a save shows a false 'could not save' Notice

1 participant